package com.example.demo.controller;

import com.example.demo.common.Encryptor;
import com.example.demo.common.Func;
import com.example.demo.common.Result;
import com.example.demo.entity.FileInfo;
import com.example.demo.exception.BusinessException;
import com.example.demo.service.UploadService;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/upload")
@Api(tags = "22、上传文件管理")
@ApiSupport(author = "罗海林", order = 22)
@Validated
@RequiredArgsConstructor
public class UploadController {

    private final UploadService uploadService;

    private final Map<String, FileInfo> fileStore = new HashMap<>();

    @PostMapping("/chunkFile")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "chunk", value = "分片文件内容"),
            @ApiImplicitParam(name = "currentChunk", value = "分片文件序号"),
            @ApiImplicitParam(name = "fileName", value = "分片文件名称")
    })
    @ApiOperation(value = "分片上传文件", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 1)
    //public Result chunkFile(@RequestBody @Validated UploadDTO uploadDTO) {
    public Result chunkFile(@RequestParam MultipartFile chunk,
                            @RequestParam int currentChunk,
                            @RequestParam String fileName) {
        String filePath = Func.getRootPath() + "/file/chunk/" + fileName;
        File chunkPath = new File(filePath);
        if (!chunkPath.exists()) {
            chunkPath.mkdirs();
        }

        String chunkTempPath = "chunk_" + currentChunk;
        File chunkFile = new File(chunkPath, chunkTempPath);

        OutputStream os = null;
        byte[] chunkByte = null;
        try {
            os = new FileOutputStream(chunkFile);
            chunkByte = chunk.getBytes();
            os.write(chunkByte);
            os.flush();
        }
        catch (Exception ex) {
            throw new BusinessException("上传分片异常:" + ex);
        }
        finally {
            if (chunkByte != null) {
                chunkByte.clone();
            }
            if (os != null) {
                try {
                    os.close();
                }
                catch (Exception ignored) {}
            }
        }

        Result res = new Result().ok();
        res.put("filePath", filePath + "/" + chunkTempPath);
        return res;
    }

    @PostMapping("/mergeChunks")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "fileName", value = "分片文件总名")
    })
    @ApiOperation(value = "合并分片文件", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 2)
    public Result mergeChunks(@RequestParam String fileName) {
        String filePath = Func.getRootPath() + "/file/chunk/" + fileName;
        File chunkPath = new File(filePath);

        String resultPath = Func.getRootPath() + "/file/chunk/result/" + fileName;
        File mergedFile = new File(resultPath);

        OutputStream os = null;
        try {
            os = new FileOutputStream(mergedFile);
            int len = chunkPath.listFiles().length;
            for (int i = 0; i < len; i++) {
                File chunkFile = new File(chunkPath, "chunk_" + i);
                Files.copy(chunkFile.toPath(), os);
                chunkFile.delete();
            }
        }
        catch (Exception ex) {
            throw new BusinessException("合并分片异常:" + ex);
        }
        finally {
            if (os != null) {
                try {
                    os.close();
                }
                catch (Exception ignored) {}
            }
            if (chunkPath != null) {
                chunkPath.delete();
            }
        }

        Result res = new Result().ok();
        return res;
    }

    @PostMapping("/uploadChunkFile")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "chunk", value = "分片文件内容"),
            @ApiImplicitParam(name = "currentChunk", value = "分片文件序号"),
            @ApiImplicitParam(name = "fileName", value = "分片文件名称"),
            @ApiImplicitParam(name = "isEnd", value = "上传结束标识")
    })
    @ApiOperation(value = "分片上传并合并文件", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 3)
    public Result uploadChunkFile(@RequestParam MultipartFile chunk,
                                  @RequestParam int currentChunk,
                                  @RequestParam String fileName,
                                  @RequestParam int isEnd) {
        try {
            uploadService.uploadChunkFile(chunk, currentChunk, fileName);
        }
        catch (Exception ex) {
            throw new BusinessException("上传分片异常:" + ex);
        }

        try {
            if (isEnd == 1) {
                uploadService.mergeChunksFile(fileName);
            }
        }
        catch (Exception ex) {
            throw new BusinessException("合并分片异常:" + ex);
        }

        Result res = new Result().ok();
        return res;
    }

    @PostMapping("/checkFile")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "上传文件")
    })
    @ApiOperation(value = "秒传大文件", response = Result.class)
    @ApiOperationSupport(author = "罗海林", order = 4)
    public Result checkFile(@RequestParam MultipartFile file) throws IOException {
        String data = file.getOriginalFilename() + file.getSize();
        String fileKey = Encryptor.hashData(data, "MD5");
        FileInfo fileInfo = fileStore.get(fileKey);
        if (fileInfo != null) {
            throw new BusinessException("文件已上传，可使用秒传");
        }

        fileInfo = new FileInfo();
        fileInfo.setId(Func.getSnowID());
        fileInfo.setFileMd5(fileKey);
        fileInfo.setFileName(file.getOriginalFilename());
        fileInfo.setFileSize(file.getSize());
        fileInfo.setFilePath(Func.getRootPath() + "/file/upload/" + file.getOriginalFilename());
        fileInfo.setInputStreamStr(file.getInputStream().toString());
        fileStore.put(fileKey, fileInfo);

        Result res = new Result().ok();
        res.put("fileInfo", fileInfo);
        return res;
    }

}
